9.3 EdeficePinGenerator PIN generation algorithm
The EdeficePinGenerator PIN generation algorithm uses the card serial number as diversification data. The PIN generation key is used to generate the PIN. If you have the card serial number, the same key that is used within MyID, and the details of the following algorithm, you can generate the same PINs as MyID.
Alternatively, you can use the user's logon name as the diversification data; this ensures that the user has the same PIN for all of their cards. To use the logon name, set the Use logon name for server PIN generation option on the PINs page of the Security Settings workflow.
9.3.1 Generating the PIN
The process for generating the PIN is as follows:
-
Use the card serial number as the input to a SHA1 hash.
This generates a 20-byte hash value.
- Truncate the 20-byte hash to the first 16 bytes. Encryption is carried out on 8-byte blocks, so we want to carry out the encryption on two blocks without padding.
-
Encrypt the hash with the PIN generation key.
- Use 3DES encryption in cipher block chaining mode. This generates a 16-byte hex value.
- You do not want any header information in the encrypted data.
- For the initialization vector, use 8 bytes of 0x00.
- Do not use any padding.
-
For each byte, divide by the alphabet size (numeric, alpha or alphanumeric) and take the remainder; in other words, <byte> modulo <alphabet size>.
As there are 16 bytes, you can generate PINs up to 16 characters long. If the PIN is 6 characters long, for example, perform this operation on the first 6 bytes in the encrypted data.
-
Use this value as a look-up in the alphabet table – see section 9.3.2, Alphabet tables.
For example, if the byte is 2C, and the alphabet size is 10 (for numeric PINs):
2C = 44 decimal
44 modulo 10 = 4
Entry 4 in the numeric table = '4'
9.3.2 Alphabet tables
Numeric
The numeric alphabet has size 10, and the following entries:
Index |
0 |
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
Value |
0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 |
For example, a lookup of 0 returns 0, and a lookup of 7 returns 7.
Note: The EdeficePinGenerator PIN generator uses a numeric alphabet only.
Alpha
The alpha alphabet has size 52, and has the following entries:
Index |
0 |
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
Value |
a | b | c | d | e | f | g | h | i | j |
Index |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
Value |
k | l | m | n | o | p | q | r | s | t |
Index |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
Value |
u | v | w | x | y | z | A | B | C | D |
Index |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
Value |
E | F | G | H | I | J | K | L | M | N |
Index |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
Value |
O | P | Q | R | S | T | U | V | W | X |
Index |
50 |
51 |
|
|
|
|
|
|
|
|
Value |
Y | Z |
|
|
|
|
|
|
|
|
For example, a lookup of 0 returns a, and a lookup of 37 returns L.
Alphanumeric
The alphanumeric alphabet has size 62, and has the following entries:
Index |
0 |
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
Value |
0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 |
Index |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
Value |
a | b | c | d | e | f | g | h | i | j |
Index |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
Value |
k | l | m | n | o | p | q | r | s | t |
Index |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
Value |
u | v | w | x | y | z | A | B | C | D |
Index |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
Value |
E | F | G | H | I | J | K | L | M | N |
Index |
50 |
51 |
52 |
53 |
54 |
55 |
56 |
57 |
58 |
59 |
Value |
O | P | Q | R | S | T | U | V | W | X |
Index |
60 |
61 |
|
|
|
|
|
|
|
|
Value |
Y | Z |
|
|
|
|
|
|
|
|
For example, a lookup of 0 returns 0, and a lookup of 37 returns B.
9.3.3 Example
If a numeric pin with a length of 6 characters is requested for a card with serial number 0000000002000304 the process is as follows:
-
The card serial number, 0000000002000304, is hashed using SHA1 to produce:
A1CB37418AF6ADB8A18E0673A2198E683D4992D6
-
This is then shortened to 16 bytes, as we want to encode two whole 8-byte blocks:
A1CB37418AF6ADB8A18E0673A2198E68
-
This hash is then 3DES CBC mode encrypted using a shared key to produce, for example:
7849DE09B259DE772EC0DCFE269E9A40
-
Each byte is used to look up into the numeric alphabet array (size 10). For a 6 character numeric pin this results in:
78 (hex) = 120 (dec) mod 10 = 0
49 (hex) = 73 (dec) mod 10 = 3
DE (hex) = 222 (dec) mod 10 = 2
09 (hex) = 9 (dec) mod 10 = 9
B2 (hex) = 178 (dec) mod 10 = 8
59 (hex) = 89 (dec) mod 10 = 9
- The PIN returned is 032989.
C# example
The following is sample code that generates PINs using C#.
using System; using System.Collections.Generic; using System.Text; using System.Security.Cryptography;
namespace PINGeneration { class Program { static void Main(string[] args) { // Alphabet and PIN size char[] alphabet = {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9'}; int alphabetsize = alphabet.Length; int pinlength = 8;
// Encryption key byte[] key = { 0x31, 0x28, 0x7A, 0x5A, 0x36, 0x26, 0x35, 0x31, 0x32, 0x71, 0x71, 0x53, 0x3D, 0x2F, 0x33, 0xA7, 0x21, 0x4C, 0x3F, 0x61, 0x44, 0x31, 0x55, 0x38 }; //Data to be encoded - device serial number string data = "1034"; //Convert to a byte array Encoding ascii = Encoding.ASCII; byte[] databytes = ascii.GetBytes(data);
// Create SHA1 hash of data SHA1 shaM = new SHA1Managed(); byte[] hash = SHA1Managed.Create().ComputeHash(Encoding.Default.GetBytes(data)); byte[] hash16 = new byte[16];
// Copy the first 16 bytes of the hash array Array.Copy(hash,hash16,16); // Set the initialisation vector to 8 bytes of 0x0 byte[] iv = {0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0 }; string ciphertext = ""; string pin = ""; string hashhex = ""; string hashhex16 = "";
// Set encryption options. TripleDESCryptoServiceProvider des = new TripleDESCryptoServiceProvider(); des.KeySize = 192; des.Key = key; des.Mode = CipherMode.CBC; des.Padding = PaddingMode.None; des.IV = iv;
// Encrypt hashed data IcryptoTransform ic = des.CreateEncryptor(); byte[] enc = ic.TransformFinalBlock(hash, 0, 16); for (int i = 0; i < enc.Length; i++) { ciphertext = ciphertext + enc[i].ToString("X2"); }
// Generate PIN from the ciphertext for (int x = 0; x < pinlength; x++) { pin = pin + alphabet[Convert.ToInt32(ciphertext.Substring(x * 2, 2),16) % alphabetsize]; } for (int i = 0; i < hash.Length; i++) { hashhex = hashhex + hash[i].ToString("X2"); } for (int i = 0; i < hash16.Length; i++) { hashhex16 = hashhex16 + hash16[i].ToString("X2"); }
Console.WriteLine("Sample PIN generation algorithm output"); Console.WriteLine(); Console.WriteLine("Alphabet size: " + alphabetsize); Console.WriteLine("Required PIN length: " + pinlength); Console.WriteLine("Data: " + data); Console.WriteLine("SHA1 hash of data: " + hashhex); Console.WriteLine("16 bytes of hash: " + hashhex16); Console.WriteLine("Encrypted: " + ciphertext); Console.WriteLine("\nPIN: " + pin); Console.WriteLine("\nPress any key to continue…"); Console.ReadKey(true); } } }